<?php


namespace App\Service\Wechat;


use App\Base\BaseConfig;

class WechatPay
{

    //这里使用商户小程序的APPId 和 KEY
    public $jsapi;//jsapi参数，格式为json
    public $qipay_confing;
    public $url;
    public $result = array();
    public $response;
    public $curl_timeout;
    public $prepay_id;
    public $ApiUser = "wx52890cfba6c32e2f";//AppId
    public $ApiPassword = "a4e195b5d3772d466f6a338b0ae89c09";//AppSecret
    public $PayAccount = "1597295291";//商户号
    public $PayAccountKey = "642b333c1fb43e0f14b7e87701431c3l";

    public $notify_url = BaseConfig::__BASE_URL__."/pay/cb/wx";//回调地址
    public $error  = "";//错误信息
    //主要用于商户支付

    #配置参数
    public $parameters = array();

    public function createOrder($openId,$body,$out_trade_no,$price,$cbStr="")
    {

        //设置接口链接
        $this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        //设置curl超时时间
        $this->curl_timeout = 20;
        /*-----------------------------必填--------------------------*/
        $this->setParameter("body",$body);										//商品描述支付平台
        $this->setParameter("out_trade_no",$out_trade_no);					    //商户订单号
        $this->setParameter("total_fee",$price);							    //总金额（微信支付以人民币“分”为单位）
        $this->setParameter("openid",$openId);							        //获取到的OPENID//todo APP支付不需要此参数
        $this->setParameter("attach",$cbStr);							        //附加数据,回调时原样返回
        $this->setParameter("notify_url",$this->notify_url);					        //通知地址
        $this->setParameter("trade_type","JSAPI");				//交易类型,小程序和公众号都是JSAPI，App开发是 APP
        $this->result = $this->getprepayId();
//        dump($this->result);
        if(isset($this->result['err_code']))
        {
            if($this->result['err_code'] != "SUCCESS"){
                if (isset($this->result["err_code_des"])) {
                    $this->error = $this->result["err_code_des"];
                }
                return false;
            }
        }else{
            //=========步骤3：使用jsapi调起支付============
            $this->setPrepayId($this->result['prepay_id']);
            $jsApiParameters = $this->getParameters($out_trade_no);
            return $jsApiParameters;
        }
    }

    public function createOrderApp($openId,$body,$out_trade_no,$price,$cbStr="")
    {

        //设置接口链接
        $this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        //设置curl超时时间
        $this->curl_timeout = 20;
        /*-----------------------------必填--------------------------*/
        $this->setParameter("body",$body);										//商品描述支付平台
        $this->setParameter("out_trade_no",$out_trade_no);					    //商户订单号
        $this->setParameter("total_fee",$price);							    //总金额（微信支付以人民币“分”为单位）
//        $this->setParameter("openid",$openId);							        //获取到的OPENID//todo APP支付不需要此参数
        $this->setParameter("attach",$cbStr);							        //附加数据,回调时原样返回
        $this->setParameter("notify_url",$this->notify_url);					        //通知地址
        $this->setParameter("trade_type","APP");							        //交易类型
        $this->result = $this->getprepayId();
        if(isset($this->result['err_code']))
        {
            if($this->result['err_code'] != "SUCCESS"){
                if (isset($this->result["err_code_des"])) {
                    $this->error = $this->result["err_code_des"];
                }
                return false;
            }
        }else{
            //=========步骤3：使用jsapi调起支付============
            $this->setPrepayId($this->result['prepay_id']);
            $jsApiParameters = $this->getParametersApp($out_trade_no);
            return $jsApiParameters;
        }
    }

    /**
     * 	作用：设置请求参数
     */
    protected function setParameter($parameter, $parameterValue)
    {
        $this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
    }

    /**
     * 	作用：去除空格
     */
    protected function trimString($value)
    {
        $ret = null;
        if (null != $value)
        {
            $ret = $value;
            if (strlen($ret) == 0)
            {
                $ret = null;
            }
        }
        return $ret;
    }

    /**
     * 获取prepay_id
     */
    protected function getprepayId()
    {
        $this->postXml();
        $this->result = $this->xmlToArray($this->response);
        return $this->result;
    }

    /**
     * 	作用：post请求xml
     */
    protected function postXml()
    {
        $xml = $this->createXml();
        $this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
        return $this->response;
    }

    /**
     * 	作用：设置标配的请求参数，生成签名，生成接口参数xml
     */
    protected function createXml()
    {
        $this->parameters["appid"] = $this->ApiUser;//公众账号ID
        $this->parameters["mch_id"] = $this->PayAccount;//商户号
        $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
        $this->parameters["sign"] = $this->getSign($this->parameters);//签名
        return  $this->arrayToXml($this->parameters);
    }

    /**
     * 	作用：产生随机字符串，不长于32位
     */
    public function createNoncestr( $length = 32 )
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str ="";
        for ( $i = 0; $i < $length; $i++ )  {
            $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
        }
        return $str;
    }

    /**
     * 	作用：生成签名
     */
    public function getSign($Obj)
    {
        foreach ($Obj as $k => $v)
        {
            $Parameters[$k] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //echo '【string1】'.$String.'</br>';
        //签名步骤二：在string后加入KEY
        $String = $String."&key=".$this->PayAccountKey;
        //echo "【string2】".$String."</br>";
        //签名步骤三：MD5加密
        $String = md5($String);
        //echo "【string3】 ".$String."</br>";
        //签名步骤四：所有字符转为大写
        $result_ = strtoupper($String);
        // echo "【result】 ".$result_."</br>";
        return $result_;
    }

    /**
     * 	作用：array转xml
     */
    protected function arrayToXml($arr)
    {

        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
            if (is_numeric($val))
            {
                $xml.="<".$key.">".$val."</".$key.">";

            }
            else
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
        }
        $xml.="</xml>";
        return $xml;
    }

    /**
     * 	作用：以post方式提交xml到对应的接口url
     */
    public function postXmlCurl($xml,$url,$second=30)
    {
        //初始化curl
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        curl_close($ch);
        //返回结果
        if($data)
        {
            return $data;
        }
        else
        {
            $error = curl_errno($ch);
            echo "curl出错，错误码:$error"."<br>";
            echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
            curl_close($ch);
            return false;
        }
    }

    /**
     * 	作用：将xml转为array
     */
    public function xmlToArray($xml)
    {
        //将XML转为array
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }

    /**
     * 	作用：设置prepay_id
     */
    function setPrepayId($prepayId)
    {
        $this->prepay_id = $prepayId;
    }

    /**
     * 	作用：设置jsapi的参数
     * 网页支付和小程序支付JSPI的返回结果
     */
    public function getParameters($out_trade_no)
    {
        $jsApiObj["appId"] = $this->ApiUser;
        $timeStamp = time();
        $jsApiObj["timeStamp"] = "$timeStamp";
        $jsApiObj["nonceStr"] = $this->createNoncestr();
        $jsApiObj["package"] = "prepay_id=$this->prepay_id";
        $jsApiObj["signType"] = "MD5";
        $jsApiObj["paySign"] = $this->getSign($jsApiObj);
        $jsApiObj["paytoken"] = md5($out_trade_no.$jsApiObj["appId"].date("Ymd"));
        $jsApiObj["out_trade_no"] = $out_trade_no;
        $jsApiObj["orderId"] = $this->prepay_id;
        return $jsApiObj;
    }

    /**
     * 	作用：为APP生成相应参数
     */
    public function getParametersApp($out_trade_no)
    {
        $nonce_str = $this->createNoncestr();
        $timeStamp = time();

        //生成签名的数据
        $tmp['appid'] = $this->ApiUser;
        $tmp['noncestr'] = $nonce_str;
        $tmp['package'] = 'Sign=WXPay';
        $tmp['partnerid'] = $this->PayAccount;
        $tmp['prepayid'] = $this->prepay_id;
        $tmp['timestamp'] = "$timeStamp";

//        返回前台的数据
        $jsApiObj["appId"] = $this->ApiUser;
        $jsApiObj["timeStamp"] = "$timeStamp";
        $jsApiObj["nonceStr"] = $nonce_str;
        $jsApiObj["package"] = 'Sign=WXPay';
        $jsApiObj["signType"] = "MD5";
        $jsApiObj["paySign"] = $this->getSign($tmp);
        $jsApiObj["sign"] = $this->getSign($tmp);
        $jsApiObj["paytoken"] = md5($out_trade_no.$jsApiObj["appId"].date("Ymd"));
        $jsApiObj["out_trade_no"] = $out_trade_no;
        $jsApiObj["orderId"] = $this->prepay_id;
        return $jsApiObj;
    }

    /**
     * 	作用：格式化参数，签名过程需要使用
     */
    function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v)
        {
            if($urlencode)
            {
                $v = urlencode($v);
            }
            //$buff .= strtolower($k) . "=" . $v . "&";
            $buff .= $k . "=" . $v . "&";
        }
        $reqPar = "";
        if (strlen($buff) > 0)
        {
            $reqPar = substr($buff, 0, strlen($buff)-1);
        }
        return $reqPar;
    }

}